package cn.butcher21;

import android.widget.Toast;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * 计算器工具类
 */
public class Calculator {

    // 是否是正确的格式
    private static boolean isRightFormat = true;

    /**
     * 获取计算结果
     * @param formula 字符串形式的算式
     * @return double类型的结果
     * @throws BracketErrorException
     */
    public static BigDecimal getResult(String formula) throws BracketErrorException {

        // 如果格式不正确，抛出异常
        if(!isRightFormat){
            throw new BracketErrorException();
        }else {
            return doAnalysis(formula);
        }

    }

    /**
     * 对算式进行分析
     * @param formula 算式
     * @return 如果算式结果正确，就返回结果
     * @throws BracketErrorException
     */
    private static BigDecimal doAnalysis(String formula) throws BracketErrorException {
        double returnValue = 0;
        LinkedList<Integer> stack = new LinkedList<Integer>();
        int curPos = 0;
        String beforePart = "";
        String afterPart = "";
        String calculator = "";
        isRightFormat = true;
        while(isRightFormat&&(formula.indexOf('(') >= 0||formula.indexOf(')') >= 0)){
            // 当前算式正确，且小括号位置大于或等于0

            // 当前位置
            curPos = 0;

            // 将算式转为字符数组进行遍历，检查有没有括号，有括号就先算括号里面的
            for(char s : formula.toCharArray()){
                if(s == '('){
                    // 左括号直接入栈
                    stack.add(curPos);
                }else if(s == ')'){
                    // 右括号入栈需要判断有没有左括号配对
                    if(stack.size() > 0){
                        beforePart = formula.substring(0, stack.getLast());
                        afterPart = formula.substring(curPos + 1);
                        calculator = formula.substring(stack.getLast() + 1, curPos);
                        formula = beforePart + doCalculation(calculator) + afterPart;
                        stack.clear();
                        break;
                    }else{
                        // 如果没有匹配到左括号，说明算式不对，抛出异常
                        isRightFormat = false;
                        throw new BracketErrorException();
                    }
                }
                curPos++;
            }
            // 如果没有清0，说明没有匹配到右括号，也说明算式不对，抛出异常
            if(stack.size() > 0){
                throw new BracketErrorException();
            }
        }

        // 如果检查完毕，没有错误，就进行计算
        if(isRightFormat){
            return doCalculation(formula);
        }
        return null;
    }

    /**
     * 计算结果
     * @param formula 算式
     * @return
     */
    private static BigDecimal doCalculation(String formula) {
        ArrayList<Double> values = new ArrayList<Double>(); // 数值
        ArrayList<String> operators = new ArrayList<String>(); // 运算符
        int curPos = 0; // 当前位置
        int prePos = 0; // 数的前下标
        int minus = 0; // 检查，防止运算符连续
        for (char s : formula.toCharArray()) {
            if ((s == '+' || s == '-' || s == 'x' || s == '÷' || s == '%') && minus !=0 && minus !=2) {
                // 扫描到了运算符，把运算符前的数放入数值列表
                values.add(Double.parseDouble(formula.substring(prePos, curPos).trim()));
                // 把运算符放入运算符列表
                operators.add("" + s);
                prePos = curPos + 1;
                minus = minus +1;
            }else{
                minus =1;
            }
            curPos++;
        }
        values.add(Double.parseDouble(formula.substring(prePos).trim()));
        char op;
        // 先算乘除法
        for (curPos = 0; curPos <= operators.size() - 1; curPos++) {
            op = operators.get(curPos).charAt(0);
            switch (op) {
                case 'x':
                    values.add(curPos, values.get(curPos) * values.get(curPos + 1));
                    values.remove(curPos + 1);
                    values.remove(curPos + 1);
                    operators.remove(curPos);
                    curPos = -1;
                    break;
                case '÷':
                    values.add(curPos, values.get(curPos) / values.get(curPos + 1));
                    values.remove(curPos + 1);
                    values.remove(curPos + 1);
                    operators.remove(curPos);
                    curPos = -1;
                    break;

                case '%':
                    values.add(curPos, values.get(curPos) % values.get(curPos + 1));
                    values.remove(curPos + 1);
                    values.remove(curPos + 1);
                    operators.remove(curPos);
                    curPos = -1;
                    break;
            }
        }

        // 再算加减法
        for (curPos = 0; curPos <= operators.size() - 1; curPos++) {
            op = operators.get(curPos).charAt(0);
            switch (op) {
                case '+':
                    values.add(curPos, values.get(curPos) + values.get(curPos + 1));
                    values.remove(curPos + 1);
                    values.remove(curPos + 1);
                    operators.remove(curPos);
                    curPos = -1;
                    break;
                case '-':
                    values.add(curPos, values.get(curPos) - values.get(curPos + 1));
                    values.remove(curPos + 1);
                    values.remove(curPos + 1);
                    operators.remove(curPos);
                    curPos = -1;
                    break;
            }
        }
        return BigDecimal.valueOf(values.get(0).doubleValue());
    }

    public static void main(String[] args) {
        try {
            System.out.println(Calculator.getResult("9999999999-5"));
        } catch (BracketErrorException e) {
            System.out.println(e.getMessage());
        }catch (NumberFormatException e){
            System.out.println(e.getMessage());
        }

    }
}
